home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Science⁄Math / VideoToolbox / VideoToolboxSources / Assign.c next >
Encoding:
Text File  |  1993-12-16  |  52.0 KB  |  1,875 lines  |  [TEXT/KAHL]

  1. /*
  2. Assign.c
  3.  
  4. Assign is a portable runtime C interpreter that reads and executes any text
  5. "assignment" file that contains only C assignments and comments, e.g.
  6.     viewingDistance=57.0;    // inches
  7.     
  8. See Assign.note for documentation.
  9.  
  10. HISTORY
  11. 7/30/91 dgp    wrote it.
  12. 8/4/91    dgp added new routines and renamed the old ones. Everything
  13.             seems to work, but has not been thoroughly tested.
  14. 8/5/91    dgp    MPW C 3.2 now compiles it without error messages. The MPW C macro processor,
  15.             contrary to Standard C, finds comment symbols inside strings and finds 
  16.             preprocessor directives even when # is not the first nonblank character.
  17. 8/24/91    dgp    Made compatible with THINK C 5.0.
  18.             Changed ReadAssignment file to return an error code instead of aborting
  19.             if it can't open the file.
  20. 8/26/91    dgp    Added SetVariable() and noted, above, that the Variable array
  21.             is terminated by an element with its "type" field set to zero. (Thanks
  22.             to Evan Relkin for pointing out the omission.)
  23. 4/1/92    dgp    Tidied up the documentation above.
  24. 4/2/92    dgp Introduced ReadAssignmentBlock() and AssignmentLineWasBlank(). 
  25.             Deleted ReadAssignments().
  26. 4/5/92    dgp    ReadAssignmentFile() now closes the file before returning.
  27. 4/17/92    dgp    Expanded the explanation of flags, as requested by Evan Relkin.
  28. 5/14/92    dgp    Expanded the explanation of stringType, "".
  29. 8/4/92    dgp    Added PrintAnAssignment(), which prints out the value of a variable,
  30.             as an assignment statement, suitable for reading by the ReadAssignment
  31.             routines.
  32. 10/24/92 dgp Eliminated double spacing that occurred after //-style comment when 
  33.             assignEchoComment was requested.
  34. 3/4/93    dgp    changed the definition of emptyString slightly so that this file could be
  35.             compiled as a code resource.
  36. 5/24/93    dgp    Assignment lines may now be continued by putting a backslash at the end of
  37.             the line.
  38.             Strings may include literal linefeeds, rather than just "\n".
  39.             Printed string assignments translate linefeeds to "\n".
  40.             Skip unknown variables unless flags&assignReportUnknown is true.
  41. 5/27/93    dgp    Added "dim" field to Variable structure, and added support for
  42.             it in all the routines.
  43.             Added floatType.
  44.             Input line length is still limited to BUFFER_SIZE (presently 512) but
  45.             strings (broken up into lines of legal length and automatically 
  46.             concatenated during reading) can be of any length that will fit
  47.             in memory.
  48. 5/28/93    dgp    Added support for hex-encoded strings.
  49.             Created new bottleneck procedure ReadLine that does all the reading.
  50. 5/31/93    dgp    assignEchoAssignments now calls PrintAnAssignment. Removed "\n" from
  51.             PrintAnAssignment. Made all Print routines return assignment count,
  52.             which is now redefined as the number of array elements or scalars
  53.             that were assigned or printed. Now parse names so that
  54.             known and unknown identifiers are treated uniformly; these new
  55.             naming rules allow only simple C identifiers joined by "." and "->".
  56. 6/1/93    dgp    introduced assignNoPrintfExit flag that causes all routines to handle
  57.             errors discreetely, simply returning an error number to the caller.
  58. 6/2/93    dgp PrintAnAssignment now prints multiple assignments per 80 character line, 
  59.             adding as many continuation lines as necessary. Quoted strings (and hex
  60.             data strings) are now broken up into 80-character lines; continuation
  61.             lines are indented four spaces. Added comment field to Variable structure
  62.             and added support for it in PrintAnAssignment. Renamed all published
  63.             enum values to begin with "assign". Renamed this file "Assign.c". Added
  64.             UnequalVariables(). CheckAVariable checks that variable name is legal.
  65. 6/5/93    dgp    Removed all Macintosh dependencies, leaving pure Standard C plus a few
  66.             C++ style comments.
  67. 6/16/93    dgp    Removed prohibition: arrays of stringType are now ok.
  68.             InitializeAVariable now calls CheckAVariable with "flags" unmodified.
  69.             CheckAVariable now checks for any illegal suffix in name.
  70.             Subscripted unknown variables are now correctly reported as "UNKNOWN" 
  71.             rather than "OUT OF BOUNDS".
  72. 6/29/93    dgp    added shortDoubleType.
  73. 7/10/93    dgp    restored Standard C compatibility by using "short double" only if
  74.             SHORT_DOUBLE_OK, since Standard C doesn't allow that type.
  75. 7/19/93    dgp    noted that file should be opened in text mode, not binary mode, so that
  76.             \r characters will be translated properly.
  77. 7/29/93    dhb & dgp Found and fixed the newline problem that appears in the MATLAB
  78.             environment, as documented above.
  79. 7/31/93    dgp    Added support for multidimensional arrays, with up to ASSIGN_DIMS
  80.             dimensions. Added SetVariableArray(), changed SetVariable() and
  81.             SetVariableFirstLast(), and deleted SetAVariableFirstLast().
  82. 8/2/93    dgp    Added dynamic allocation of ptrType arrays, based on scanning the current
  83.             gulp of the assign file.
  84. 8/12/93    dgp    Renamed "Variable" to "Description". Renamed most of the routines. 
  85.             The header file Assign92.h provides for backward compatibility.
  86. 8/21/93    dhb,dgp    For compatibility with MATLAB we avoid using int arguments in stdio 
  87.             calls because when MATLAB is true we'll be using MPW's stdio
  88.             library, and its int is 4 bytes whereas the THINK C int is 2 bytes.
  89.         dhb,jms    Changed "wa" in fopen in PrintAssignmentsToFile to "a".
  90.         dhb,jms,dgp Moved the NL and NEWLINE macros into VideoToolbox.h.
  91. 9/2/93    dgp    Expanded documentation of FreePtrVariables. Fixed minor bugs
  92.             in PrintAVariable for hex encoded Ptr variables so that dimensionality
  93.             of what was written is recovered when it's read.
  94. 9/7/93    dgp    Introduced STDIO_INT.
  95. 9/7/93    dhb,jms,dgp    Added OpenCalFileWrite, OpenCalFileRead, OpenCalFileReadAndCheck,
  96.             AppendDescriptions, CopyDescriptions, AllocateDescriptions,
  97.             FreeDescriptions, NumberOfDescriptions, NullDescription, and 
  98.             IsNullDescription.
  99. 9/9/93    dhb    Added ReadAssignmentStream, to read the rest of a file stream as a single
  100.             gulp.
  101. 9/11/93    dgp    rewrote ReadAssignmentFile to just open the file and call 
  102.             ReadAssignmentStream.
  103. 9/12/93    dhb    Changed STDIO_INT to PRINTF_INT, added inclusion of VideoToolbox
  104.             when MATLAB is on.
  105. 9/15/93    dgp    Moved documentation to Assign.note.
  106. 9/16/93    dgp    Merged Assign.h into VideoToolbox.h.
  107. */
  108. #include "VideoToolbox.h"
  109. #include <assert.h>
  110. #include <ctype.h>
  111. #include <stdarg.h>
  112. #include <stdio.h>
  113. #include <stdlib.h>
  114. #include <string.h>
  115. /*
  116. IsNan and IsFinite--see VideoToolbox.h--should work on most Intel and Motorola
  117. processors. If IsNan and IsFinite don't work on your computer, don't fret. They
  118. are used solely by UnequalDescribedVarPair. A quick fix is to use the following
  119. definitions, the only bad effect of which will be that comparisons of NANs may
  120. appear spuriously unequal.
  121. */
  122. #ifndef IsFinite
  123.     #define IsNan(x) 0
  124.     #define IsFinite(x) 1
  125. #endif
  126. Description SetAVariable(short type,void *ptr,char *name,unsigned long dim
  127.     ,const char *comment);    /* old style */
  128.  
  129. /* the following are private, not intended for use outside this file */
  130. static int lineWasBlank=1;    /* used in ReadAssignmentLine(),    */
  131.             /* NextTokenInThisOrNextLine(), and AssignmentLineWasBlank().*/
  132. #define IsIntegral(type) ((type)>=charType&&(type)<=unsignedLongType || (type)>=charPtrType&&(type)<=unsignedLongPtrType)
  133. #define IsFloating(type) ((type)>=floatType&&(type)<=doubleType || (type)>=floatPtrType&&(type)<=doublePtrType)
  134. #define IsString(type) (type==stringType)
  135. #define IsPtr(type) ((type)>=charPtrType&&(type)<=doublePtrType)
  136. long CheckADescription(Description *d,const char *here,short flags);
  137. long CheckDescriptions(Description d[],const char *here,short flags);
  138. void FreeAPtrDescription(Description *d,short flags);
  139. void FreeAStringDescription(Description *d,short flags);
  140. char *GetName(char **sPtr,short flags);
  141. char *GetQuotedString(FILE *stream,char *lineBuffer,char *s,char **sPtr,short flags);
  142. long HexAssignment(FILE *stream,char *lineBuffer,char **sPtr,Description *d
  143.     ,int subscriptDims,long index,long *bytesPtr,short flags);
  144. char *NextToken(FILE *stream,char *lineBuffer,char *s,short flags);
  145. char *NextTokenInLine(FILE *stream,char *lineBuffer,char *s,short flags);
  146. char *NextTokenInThisOrNextLine(FILE *stream,char *lineBuffer,char *s,short flags);
  147. int ParseName(char **sPtr);
  148. void ParsingError(char *s,char *format,...);
  149. long PrintAnAssignmentOfElement(FILE *stream,Description *d,long index,short flags);
  150. void PrintQuotedString(FILE *stream,short *lineLengthPtr,const char *s);
  151. char *ReadLine(char *lineBuffer,size_t bufferSize,FILE *stream,short flags);
  152. double strtodN(char *s,char **sPtr);
  153. long strtolN(char *s,char **sPtr,short flags);
  154. unsigned long strtoulN(char *s,char **sPtr,short flags);
  155. char strtoc(char *s,char **sPtr);
  156. #define streq2(s1,s2) (strncmp(s1,s2,strlen(s2))==0)
  157. #define streq(s1,s2) (strcmp(s1,s2)==0)
  158. long Dims(Description *d);
  159. char *ElementName(char *s,Description *d,long index);
  160. void *ElementPtr(Description *d,long index);
  161. long Elements(Description *d);
  162. void ElementSubscript(Description *d,long index,long sub[ASSIGN_DIMS]);
  163. long ElementIndex(Description *d,long sub[ASSIGN_DIMS]);
  164. int GetDimensions(Description *d,int subscriptDims,long subscript[ASSIGN_DIMS],short flags);
  165. int AllocateDescribedPtrVars(Description description[],const char *here,short flags);
  166.  
  167. #ifndef TRUE
  168.     #define FALSE    0
  169.     #define TRUE    1
  170. #endif
  171. #define TOLERANCE    1e-6    /* fractional difference between two "equal" floats */
  172. #define BUFFER_SIZE 512
  173. #define ECHO_ASSIGNMENTS    (flags&assignEchoAssignments && !(flags&assignScan))
  174. #define ECHO_COMMENTS        (flags&assignEchoComments && !(flags&assignScan))
  175. #define ECHO_FILE            (flags&assignEchoFile && !(flags&assignScan))
  176. #define PRINTF_EXIT            !(flags&assignNoPrintfExit)
  177. short typeSize[]={0
  178.     ,sizeof(char),sizeof(unsigned char)
  179.     ,sizeof(short),sizeof(unsigned short)
  180.     ,sizeof(long),sizeof(unsigned long)
  181.     ,sizeof(float)
  182.     #if SHORT_DOUBLE_OK
  183.         ,sizeof(short double)
  184.     #endif
  185.     ,sizeof(double)
  186.     ,sizeof(char),sizeof(unsigned char)
  187.     ,sizeof(short),sizeof(unsigned short)
  188.     ,sizeof(long),sizeof(unsigned long)
  189.     ,sizeof(float)
  190.     #if SHORT_DOUBLE_OK
  191.         ,sizeof(short double)
  192.     #endif
  193.     ,sizeof(double)
  194.     ,sizeof(char *)
  195.     ,0};
  196. char typeName[][20]={"none"
  197.     ,"char","unsigned char","short","unsigned short"
  198.     ,"long","unsigned long","float"
  199.     #if SHORT_DOUBLE_OK
  200.         ,"short double"
  201.     #endif
  202.     ,"double"
  203.     ,"char *","unsigned char *","short *","unsigned short *"
  204.     ,"long *","unsigned long *","float *"
  205.     #if SHORT_DOUBLE_OK
  206.         ,"short double *"
  207.     #endif
  208.     ,"double *"
  209.     ,"string"
  210.     ,"unknown"};
  211. enum{assignNeedMore=0x4000,assignAlreadyChecked=0x2000
  212. ,assignScan=0x100,assignLocalCall=0x200
  213. };/*flags used only in this file*/
  214.  
  215. /*
  216. ROUTINE: OpenCalFileReadAndCheck
  217. PURPOSE:
  218.     Open up a calibration file for reading.
  219.     
  220.     If the file exists in the current directory, it is used.
  221.     If not, then if it exists in the preferences folder, it used.
  222. */
  223.  
  224. FILE *OpenCalFileReadAndCheck(char *filename)
  225. {
  226.     FILE *stream;
  227.     
  228.     stream = OpenCalFileRead(filename);
  229.     if (stream == NULL)
  230.         PrintfExit("OpenCalFileReadAndCheck: cannot open file \"%s\"",filename);
  231.     return(stream);    
  232. }
  233.  
  234. /*
  235. ROUTINE: OpenCalFileRead
  236. PURPOSE:
  237.     Open up a calibration file for reading.  Don't abort on error.
  238.     
  239.     If the file exists in the current directory, it is used.
  240.     If not, then if it exists in the preferences folder, it used.
  241. */
  242.  
  243. FILE *OpenCalFileRead(char *filename)
  244. {
  245.     FILE *stream;
  246.     
  247.     /* Try the current directory */
  248.     stream = fopen(filename,"r");
  249.     if (stream != NULL) return(stream);
  250.     
  251.     /* Try the preferences folder */
  252.     OpenPreferencesFolder();
  253.     stream = fopen(filename,"r");
  254.     ClosePreferencesFolder();
  255.     return(stream);    
  256. }
  257.  
  258. /*
  259. ROUTINE: OpenCalFileWrite
  260. PURPOSE:
  261.     Open up a calibration file for appending.
  262.     
  263.     If the file exists in the current directory, it is used.
  264.     If not, then if it exists in the Preferences folder, it used.
  265.     If not, the file is created in the Preferences folder.
  266. */
  267.  
  268. FILE *OpenCalFileWrite(char *filename)
  269. {
  270.     FILE *stream;
  271.     
  272.     /* Try in the current directory */
  273.     stream = fopen(filename,"r");
  274.     if (stream != NULL) {
  275.         fclose(stream);
  276.         stream = fopen(filename,"a");
  277.         if (stream == NULL)
  278.             PrintfExit("OpenCalFileWrite: cannot reopen file \"%s\"",filename);
  279.         return(stream);
  280.     }
  281.     
  282.     /* Try the Preferences folder */
  283.     OpenPreferencesFolder();
  284.     stream = fopen(filename,"r");
  285.     ClosePreferencesFolder();
  286.     if (stream != NULL) {
  287.         fclose(stream);
  288.         OpenPreferencesFolder();
  289.         stream = fopen(filename,"a");
  290.         ClosePreferencesFolder();
  291.         if (stream == NULL)
  292.             PrintfExit("OpenCalFileWrite: cannot reopen file \"%s\" in Preferences folder"
  293.                 ,filename);
  294.         return(stream);
  295.     }
  296.     
  297.     /* Create it in the Preferences folder */
  298.     OpenPreferencesFolder();
  299.     stream = fopen(filename,"a");
  300.     ClosePreferencesFolder();
  301.     if (stream == NULL)
  302.         PrintfExit("OpenCalFileWrite: cannot create file \"%s\" in Preferences folder.",filename);
  303.     return(stream);
  304. }
  305.  
  306. /*
  307. ROUTINE: AppendDescriptions
  308. PURPOSE:
  309.     Appends the second descriptions array onto the end of the first,
  310.     which is reallocated with more space.
  311.     
  312.     The source array is not freed; the caller should do that.
  313. */
  314. void AppendDescriptions(Description **d,Description *s)
  315. {
  316.     Description *dTemp;
  317.     long n1, n2, n;
  318.     
  319.     n1 = NumberOfDescriptions(*d);
  320.     n2 = NumberOfDescriptions(s);
  321.     n = n1+n2;
  322.     dTemp = AllocateDescriptions(n);
  323.     CopyDescriptions(dTemp,*d);
  324.     CopyDescriptions(dTemp+n1,s);
  325.     FreeDescriptions(*d);
  326.     *d=dTemp;
  327. }
  328.  
  329. /*
  330. ROUTINE: CopyDescriptions
  331. PURPOSE:
  332.     Copy one null-terminated array of descriptions to another, which is assumed to
  333.     be big enough.
  334. */
  335. void CopyDescriptions(Description *d,Description *s)
  336. {
  337.     long i = 0;
  338.     
  339.     while ( !IsNullDescription(s[i]) ) {
  340.         d[i]=s[i];
  341.         i++;
  342.     }
  343.     d[i]=NullDescription();
  344. }
  345.  
  346. /*
  347. ROUTINE: AllocateDescriptions
  348. PURPOSE:
  349.     Allocate space for variable descriptions.
  350.     Adds one to the passed size to hold the null descriptor.
  351.     Nulls the first element.
  352. */
  353. Description *AllocateDescriptions(long n)
  354. {
  355.     Description *d;
  356.  
  357.     d = calloc(n+1,sizeof(Description));
  358.     if (d == NULL) {
  359.         PrintfExit("AllocateDescription: memory allocation failure.");
  360.     }
  361.     d[0]=NullDescription();
  362.     return(d);
  363. }
  364.  
  365.  
  366. /*
  367. ROUTINE: FreeDescriptions
  368. PURPOSE:
  369.     Free the description space.  Does not affect the described variables
  370.     themselves.
  371. */
  372. void FreeDescriptions(Description *d)
  373. {
  374.     free(d);    
  375. }
  376.  
  377. /*
  378. ROUTINE: NumberOfDescriptions
  379. PURPOSE:
  380.     Find the size of an array of descriptions.
  381.     
  382.     Does not count the trailing null description.
  383. */
  384. long NumberOfDescriptions(Description *d)
  385. {
  386.     long i=0;
  387.     
  388.     while ( !IsNullDescription(d[i]) ) i++;
  389.     return(i);
  390. }
  391.  
  392. /*
  393. ROUTINE: NullDescription
  394. PURPOSE:
  395.     Returns a null description.
  396.     Standard C specifies that static variables are initialized to zero.
  397. */
  398. Description NullDescription(void)
  399. {
  400.     static Description d;
  401.     return d;
  402. }
  403.  
  404. Description Describe(short type,void *ptr,char *name,const char *comment)
  405. {
  406.     static Description var;
  407.  
  408.     var=DescribeArray(type,ptr,name,comment,0L);
  409.     if(IsPtr(type)) var.sizedOnce=var.sized=0;
  410.     return var;
  411. }
  412.  
  413. Description DescribeArray(short type,void *ptr,char *name,const char *comment,...)
  414. /* WARNING: the dimensions must be (long) and the last argument must be 0L. */
  415. {
  416.     va_list args;
  417.     static Description var;
  418.     int i;
  419.     long dim;
  420.     
  421.     var.name=name;
  422.     var.ptr=ptr;
  423.     var.type=type;
  424.     var.firstElement=0;
  425.     for(i=0;i<ASSIGN_DIMS;i++)var.dim[i]=0;
  426.     var.comment=comment;
  427.     va_start(args,comment);
  428.     for(i=0;;i++){
  429.         dim=va_arg(args,long);
  430.         if(dim==0)break;
  431.         if(i>=ASSIGN_DIMS)PrintfExit("Describe/Array/FirstLast: "
  432.             "too many dimensions (or missing final 0L) for \"%s\".\n",var.name);
  433.         var.dim[i]=dim;
  434.     }
  435.     va_end(args);
  436.     if(var.type!=0 && var.ptr==NULL)
  437.         PrintfExit("Describe/Array/FirstLast: \"%s\" ptr is NULL.\n",var.name);
  438.     /* Note: we're clearing the malloced flag, which is the only safe assumption. */
  439.     var.malloced=0;
  440.     var.sizedOnce=var.sized=1;
  441.     return var;
  442. }
  443. Description DescribeFirstLast(short type,void *ptr,char *name
  444.     ,const char *comment,long firstElement,long lastElement)
  445. {
  446.     static Description var;
  447.     long dim;
  448.     
  449.     dim=1+lastElement-firstElement;
  450.     var=DescribeArray(type,ptr,name,comment,dim,0L);
  451.     var.firstElement=firstElement;
  452.     return var;
  453. }
  454. Description SetAVariable(short type,void *ptr,char *name,unsigned long dim
  455.     ,const char *comment)
  456. /* Old style, retained solely for compatibility */
  457. {
  458.     if(dim==0)return Describe(type,ptr,name,comment);
  459.     else return DescribeArray(type,ptr,name,comment,dim,0L);
  460. }
  461. int AllocateDescribedPtrVars(Description description[],const char *here,short flags)
  462. {
  463.     register Description *d;
  464.     
  465.     for(d=description;d->type!=0;d++){
  466.         if(IsPtr(d->type) && d->sizedOnce && !d->malloced){
  467.             assert(d->ptr!=NULL);
  468.             *(void **)d->ptr=malloc(Elements(d)*typeSize[d->type]);
  469.             if(*(void **)d->ptr==NULL){
  470.                 if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes for \"%s\".\n"
  471.                     ,here,Elements(d)*typeSize[d->type],d->name);
  472.                 else return assignMemoryError;
  473.             }
  474.             d->sized=d->malloced=1;
  475.             InitializeADescribedVar(d,flags);
  476.         }
  477.     }
  478.     return 0;
  479. }
  480.  
  481. long CheckDescriptions(Description d[],const char *here,short flags)
  482. {
  483.     long error;
  484.     
  485.     if(flags&assignAlreadyChecked)return 0;
  486.     for(;d->type!=0;d++){
  487.         error=CheckADescription(d,here,flags);
  488.         if(error)return error;
  489.     }
  490.     return 0;
  491. }
  492.  
  493. long CheckADescription(Description *d,const char *here,short flags)
  494. {
  495.     char *s,*sOk;
  496.     
  497.     if(flags&assignAlreadyChecked)return 0;
  498.     if(d->type<=0 || d->type>=unknownType){
  499.         if(PRINTF_EXIT)PrintfExit("%s: \"%s\" has unknown type %ld.\n"
  500.             ,here,d->name,(long)d->type);
  501.         else return assignVariableError;
  502.     }
  503.     if(!IsPtr(d->type) && d->ptr==NULL){
  504.         if(PRINTF_EXIT)PrintfExit("%s: \"%s\" ptr is NULL.\n"
  505.             ,here,d->name);
  506.         else return assignVariableError;
  507.     }
  508.     /* Squeeze any space out of the name */
  509.     for(s=sOk=d->name;*s!=0;s++)if(!isspace(*s))*sOk++=*s;
  510.     *sOk=0;
  511.     /* Check name's syntax */
  512.     s=d->name;
  513.     if(ParseName(&s) || *s!=0){
  514.         if(PRINTF_EXIT)PrintfExit("%s: illegal name \"%s\".\n"
  515.             ,here,d->name);
  516.         else return assignVariableError;
  517.     }
  518.     return 0;
  519. }
  520.  
  521. int UnequalDescribedVars(Description d1[],Description d2[],short flags)
  522. /*
  523. Compares the data pointed to by the two Description arrays and returns
  524. true if the structs are legal and the data are equal, false otherwise.
  525. */
  526. {
  527.     int error;
  528.     
  529.     for(;d1->type!=0 && d2->type!=0;d1++,d2++){
  530.         error=UnequalDescribedVarPair(d1,d2,flags);
  531.         if(error)return error;
  532.     }
  533.     return 0;
  534. }
  535.  
  536. long Elements(Description *d)
  537. {
  538.     long elements=1;
  539.     int i;
  540.  
  541.     for(i=0;i<ASSIGN_DIMS && d->dim[i]>0;i++)elements*=d->dim[i];
  542.     return elements;
  543. }
  544. long Dims(Description *d)
  545. {
  546.     int i;
  547.  
  548.     for(i=0;i<ASSIGN_DIMS && d->dim[i]>0;i++);
  549.     return i;
  550. }
  551. void *ElementPtr(Description *d,long index)
  552. /* Returns NULL if PtrType array not allocated. */
  553. {
  554.     char *ptr;
  555.     
  556.     if(IsPtr(d->type)){
  557.         assert(d->ptr!=NULL);
  558.         ptr=*(void **)d->ptr;
  559.     }else ptr=d->ptr;
  560.     if(ptr!=NULL && d->dim[0]>0){
  561.         index+=d->firstElement;
  562.         ptr+=index*typeSize[d->type];
  563.     }
  564.     return ptr;
  565. }
  566. char *ElementName(char *s,Description *d,long index)
  567. {
  568.     long sub[ASSIGN_DIMS];
  569.     int i;
  570.     
  571.     ElementSubscript(d,index,sub);
  572.     sprintf(s,"%s",d->name);
  573.     for(i=0;i<Dims(d);i++)sprintf(s,"%s[%ld]",s,sub[i]);
  574.     return s;
  575. }
  576. char *DescriptionNameDimensions(Description *d);
  577. char *DescriptionNameDimensions(Description *d)
  578. {
  579.     long sub[ASSIGN_DIMS];
  580.     int i;
  581.     static char s[64];
  582.     
  583.     sprintf(s,"%s",d->name);
  584.     for(i=0;d->dim[i]>0;i++)sprintf(s,"%s[%ld]",s,d->dim[i]);
  585.     assert(strlen(s)<sizeof(s));
  586.     return s;
  587. }
  588. void ElementSubscript(Description *d,long index,long sub[ASSIGN_DIMS])
  589. {
  590.     int i;
  591.  
  592.     for(i=0;i<ASSIGN_DIMS;i++)sub[i]=0;
  593.     if(Dims(d)){
  594.         for(i=Dims(d)-1;i>=0;i--){
  595.             sub[i]=index%d->dim[i];
  596.             index/=d->dim[i];
  597.         }
  598.         sub[Dims(d)-1]+=d->firstElement;
  599.     }
  600. }
  601. long ElementIndex(Description *d,long sub[ASSIGN_DIMS])
  602. {
  603.     int i;
  604.     long index=0;
  605.  
  606.     if(Dims(d)){
  607.     sub[Dims(d)-1]-=d->firstElement;
  608.     for(i=0;i<Dims(d);i++){
  609.         index*=d->dim[i];
  610.         index+=sub[i];
  611.     }
  612.     sub[Dims(d)-1]+=d->firstElement;
  613.     }
  614.     return index;
  615. }
  616. int GetDimensions(Description *d,int subscriptDims,long subscript[ASSIGN_DIMS],short flags)
  617. {
  618.     short i;
  619.     
  620.     if(d->sizedOnce && Dims(d)!=subscriptDims)
  621.         return assignInconsistentDimensionsError;
  622.     if(!d->sized){
  623.         for(i=0;i<subscriptDims;i++)
  624.             if(d->dim[i]<subscript[i]+1)d->dim[i]=subscript[i]+1;
  625.         d->sizedOnce=1;
  626.     }
  627.     return 0;
  628. }
  629.  
  630. int UnequalDescribedVarPair(Description *d1,Description *d2,short flags)
  631. /*
  632. Compares the data pointed to by the two Description structs and returns true if the
  633. structs are legal and the data are equal, false otherwise. Comparison of floats
  634. and doubles allows a tolerance of +/- one part in a million--because converting
  635. to and from decimal may lose some precision--and ignores any NANs' indices (NAN04
  636. vs NANFF)--because they're not preserved.
  637. */
  638. {
  639.     unsigned char *p1,*p2;
  640.     long i,size,elements;
  641.     int error;
  642.     double a,b,e;
  643.     static const char here[]="UnequalDescribedVarPair";
  644.     char name[32+16*ASSIGN_DIMS];
  645.     
  646.     /* Squeeze space out of names */
  647.     error=CheckADescription(d1,here,flags);
  648.     if(error<0)return error;
  649.     error=CheckADescription(d2,here,flags);
  650.     if(error<0)return error;
  651.     if(d1->type!=d2->type
  652.         || !streq(d1->name,d2->name) 
  653.         || d1->firstElement!=d2->firstElement
  654.         || !streq(d1->comment,d2->comment))
  655.             {error=assignInconsistentDescriptionsError;goto done;}
  656.     for(i=0;i<ASSIGN_DIMS;i++)if(d1->dim[i]!=d2->dim[i])
  657.         {error=assignInconsistentDescriptionsError;goto done;}
  658.     p1=ElementPtr(d1,0);
  659.     p2=ElementPtr(d2,0);
  660.     size=typeSize[d1->type];
  661.     elements=Elements(d1);
  662.     if(!IsString(d1->type)){
  663.         if(memcmp(p1,p2,size*elements)!=0){
  664.             if(!IsFloating(d1->type)){error=assignUnequalDataError;goto done;}
  665.             for(i=0;i<elements;i++){
  666.                 switch(d1->type){
  667.                 case floatType:
  668.                     a=*(float *)p1;
  669.                     b=*(float *)p2;
  670.                     break;
  671.                 #if SHORT_DOUBLE_OK
  672.                     case shortDoubleType:
  673.                         a=*(short double *)p1;
  674.                         b=*(short double *)p2;
  675.                         break;
  676.                 #endif
  677.                 case doubleType:
  678.                     a=*(double *)p1;
  679.                     b=*(double *)p2;
  680.                     break;
  681.                 }
  682.                 /*  compare doubles */
  683.                 /*  ignore NaN type, since it's not preserved */
  684.                 if(IsNan(a)==0 || IsNan(b)==0){
  685.                     if(!IsFinite(a) || !IsFinite(b))
  686.                         {error=assignUnequalDataError;goto done;}
  687.                     e=a/b-1.0;
  688.                     if(e>TOLERANCE || e<-TOLERANCE)
  689.                         {error=assignUnequalDataError;goto done;}
  690.                 }
  691.                 p1+=size;
  692.                 p2+=size;
  693.             }
  694.         }
  695.     }else for(i=0;i<elements;i++){                            /* compare strings */
  696.         if(*(char **)p1!=*(char **)p2){
  697.             if((*(char **)p1==NULL) || (*(char **)p2==NULL)
  698.                 || !streq(*(char **)p1,*(char **)p2))
  699.                     {error=assignUnequalDataError;goto done;}
  700.         }
  701.         p1+=size;
  702.         p2+=size;
  703.     }
  704.     return 0;
  705. done:
  706.     if(!(flags&assignNoPrintfExit))switch(error){
  707.     case assignInconsistentDescriptionsError:
  708.         PrintfExit("%s: the two Descriptions of \"%s\" are inconsistent.\n"
  709.             ,here,d1->name);
  710.     case assignUnequalDataError:
  711.         PrintfExit("%s: the two instances of \"%s\" have significantly different values.\n"
  712.             ,here,ElementName(name,d1,i));
  713.     }else return error;
  714. }
  715.  
  716. void FreeAPtrDescription(Description *d,short flags)
  717. {
  718.     int i;
  719.     
  720.     if(IsPtr(d->type)){
  721.         if(d->malloced)free(*(void **)d->ptr);
  722.         *(void **)d->ptr=NULL;
  723.         for(i=0;i<ASSIGN_DIMS;i++)d->dim[i]=0;
  724.         d->sizedOnce=d->sized=d->malloced=0;
  725.     }
  726. }
  727. void FreeAStringDescription(Description *d,short flags)
  728. {
  729.     static const char emptyString[]="";
  730.     int i;
  731.     
  732.     if(IsString(d->type)){
  733.         if(d->malloced && !d->dim[0])free(*(char **)d->ptr);
  734.         *(const char **)d->ptr=emptyString;
  735.         d->malloced=0;
  736.     }
  737. }
  738. void FreeDescribedPtrVars(Description d[],short flags)
  739. {
  740.     for(;d->type!=0;d++)FreeAPtrDescription(d,flags);
  741. }
  742. void FreeADescribedVar(Description *d,short flags)
  743. {
  744.     FreeAPtrDescription(d,flags);
  745.     FreeAStringDescription(d,flags);
  746. }
  747. void FreeDescribedVars(Description d[],short flags)
  748. {
  749.     for(;d->type!=0;d++)FreeADescribedVar(d,flags);
  750. }
  751. void KeepDescribedVars(Description d[],short flags)
  752. {
  753.     for(;d->type!=0;d++)d->malloced=0;
  754. }
  755. void KeepADescribedVar(Description *d,short flags)
  756. {
  757.     d->malloced=0;
  758. }
  759.  
  760. long FindDescription(Description d[],void *ptr,short flags)
  761. {
  762.     long i;
  763.     
  764.     for(i=0;d[i].type!=0;i++)if(ptr==d[i].ptr)return i;
  765.     if(PRINTF_EXIT)PrintfExit("FindDescription: couldn't find your variable.\n");
  766.     else return assignCouldntFindDescription;
  767. }
  768. long FindDescribedDim(Description d[],void *ptr,int i,short flags)
  769. {
  770.     long n;
  771.     
  772.     n=FindDescription(d,ptr,flags);
  773.     if(n<0)return n;
  774.     if(i<0 || i>=ASSIGN_DIMS)return 0;
  775.     return d[n].dim[i];
  776. }
  777.  
  778. long InitializeDescribedVars(Description d[],short flags)
  779. {
  780.     long j,n=0;
  781.     
  782.     for(;d->type!=0;d++){
  783.         j=InitializeADescribedVar(d,flags);
  784.         if(j<0)return j;
  785.         else n+=j;
  786.     }
  787.     return n;
  788. }
  789. long InitializeADescribedVar(Description *d,short flags)
  790. {
  791.     static const char here[]="InitializeADescribedVar";
  792.     long error;
  793.     
  794.     error=CheckADescription(d,here,flags);
  795.     if(error)return error;
  796.     if(IsPtr(d->type) && !d->malloced) *(void **)d->ptr=NULL;
  797.     if(IsPtr(d->type) || d->dim[0]){
  798.         Description w;
  799.         long i,j,n=0,elements;
  800.         
  801.         if(ElementPtr(d,0)==NULL)return n;
  802.         w=*d;
  803.         if(IsPtr(w.type))w.type+=charType-charPtrType;
  804.         for(i=0;i<ASSIGN_DIMS;i++)w.dim[i]=0;
  805.         w.sizedOnce=w.sized=1;
  806.         if(IsString(w.type))w.malloced=0;
  807.         w.firstElement=0;
  808.         elements=Elements(d);
  809.         for(i=0;i<elements;i++){
  810.             w.ptr=ElementPtr(d,i);
  811.             j=InitializeADescribedVar(&w,flags);
  812.             if(j<0)return j;
  813.             n+=j;
  814.         }
  815.         return n;
  816.     }
  817.     switch(d->type){
  818.     case charType:
  819.     case unsignedCharType:
  820.         *(char *)d->ptr=0;
  821.         break;
  822.     case shortType:
  823.     case unsignedShortType:
  824.         *(short *)d->ptr=0;
  825.         break;
  826.     case longType:
  827.     case unsignedLongType:
  828.         *(long *)d->ptr=0;
  829.         break;
  830.     case floatType:
  831.         *(float *)d->ptr=NAN;
  832.         break;
  833.     #if SHORT_DOUBLE_OK
  834.         case shortDoubleType:
  835.             *(short double *)d->ptr=NAN;
  836.             break;
  837.     #endif
  838.     case doubleType:
  839.         *(double *)d->ptr=NAN;
  840.         break;
  841.     case stringType:
  842.         FreeAStringDescription(d,flags);
  843.         break;
  844.     default:
  845.         /*  it shouldn't be possible to arrive here */
  846.         return assignVariableError;
  847.     }
  848.     return 1;
  849. }
  850.  
  851. long PrintAssignmentsToFile(const char *filename,Description d[],short flags)
  852. {
  853.     long n=0;
  854.     FILE *stream;
  855.     
  856.     stream=fopen(filename,"a");
  857.     if(stream==NULL)return assignFileError;
  858.     n=PrintAssignments(stream,d,flags);
  859.     fprintf(stream,NL);    /*  Add blank line to separate blocks */
  860.     fclose(stream);
  861.     return n;
  862. }
  863.  
  864. long PrintAssignments(FILE *stream,Description d[],short flags)
  865. {
  866.     Description *vStart=d;
  867.     long n=0,i;
  868.     
  869.     while(d->type!=0){
  870.         i=PrintAnAssignment(stream,d,flags);
  871.         if(i>0)fprintf(stream,NL);    /* new line */
  872.         if(i<0)return i;
  873.         n+=i;
  874.         d++;
  875.     }
  876.     return n;
  877. }
  878.  
  879. long PrintAnAssignmentOfElement(FILE *stream,Description *d,long index,short flags)
  880. {
  881.     Description w;
  882.     long n,i;
  883.     static const char here[]="PrintAnAssignmentOfElement";
  884.     
  885.     if(index<0 || index>=Elements(d))return assignSubscriptBoundsError;
  886.     w=*d;
  887.     for(i=0;i<ASSIGN_DIMS;i++)w.dim[i]=0;
  888.     w.firstElement=0;
  889.     w.comment=NULL;
  890.     w.name=(char *)malloc(strlen(w.name)+16*Dims(d));
  891.     if(w.name==NULL){
  892.         if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes.\n"
  893.             ,here,(long)strlen(w.name)+16*Dims(d));
  894.         else return assignMemoryError;
  895.     }
  896.     ElementName(w.name,d,index);
  897.     w.ptr=ElementPtr(d,index);
  898.     if(IsPtr(w.type))w.type+=charType-charPtrType;
  899.     if(IsString(w.type))w.malloced=0;
  900.     /*  Turn off checking, 'cause subscripted name is illegal. */
  901.     n=PrintAnAssignment(stream,&w,flags|assignAlreadyChecked);
  902.     free(w.name);
  903.     return n;
  904. }
  905.  
  906. long PrintAnAssignment(FILE *stream,Description *d,short flags)
  907. {
  908.     short hexEncode;
  909.     char *string,*s,name[32+16*ASSIGN_DIMS];
  910.     static const char here[]="PrintAnAssignment";
  911.     long index,n=0,error,row;
  912.     static short lineLength;/* zeroed before printing array or string and after \n. */
  913.     Description w;
  914.     
  915.     error=CheckADescription(d,here,flags);
  916.     if(error<0)return error;
  917.     if(ElementPtr(d,0)==NULL)return n;
  918.     if(IsPtr(d->type) || d->dim[0]){
  919.         if(Dims(d))row=d->dim[Dims(d)-1];
  920.         else row=1;
  921.         hexEncode=IsIntegral(d->type) && !(flags&assignNoHexInts)
  922.             &&(row>2+typeSize[d->type]);
  923.         hexEncode|=IsFloating(d->type)&&(flags&assignHexFloats);
  924.         if(hexEncode){
  925.             for(index=0;index<Elements(d);index+=row){
  926.                 lineLength=0;
  927.                 w=*d;
  928.                 if(IsString(w.type))w.malloced=0;
  929.                 if(row>1)w.dim[Dims(d)-1]=0;
  930.                 ElementName(name,&w,index/row);
  931.                 lineLength+=fprintf(stream,"%s=",name);
  932.                 string=BinaryToHex(row*typeSize[d->type]
  933.                     ,ElementPtr(d,index),NULL);
  934.                 if(string==NULL){
  935.                     if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes.\n"
  936.                         ,here,2L*row*typeSize[d->type]);
  937.                     else return assignMemoryError;
  938.                 }
  939.                 PrintQuotedString(stream,&lineLength,string);
  940.                 free(string);
  941.                 lineLength+=fprintf(stream,";");
  942.                 if(index<Elements(d)-row)lineLength+=fprintf(stream,"\\" NL);
  943.             }
  944.             n=Elements(d);
  945.         }else{
  946.             long i,assignLength,oldLineLength,elements;
  947.             
  948.             lineLength=0;
  949.             elements=Elements(d);
  950.             for(i=0;i<elements;i++){
  951.                 oldLineLength=lineLength;
  952.                 n+=PrintAnAssignmentOfElement(stream,d,i,flags);
  953.                 assignLength=lineLength-oldLineLength;
  954.                 if(lineLength+assignLength+1>=80 && i<elements-1){
  955.                     fprintf(stream,"\\" NL);
  956.                     lineLength=0;
  957.                     lineLength+=fprintf(stream,"    ");
  958.                 }
  959.             }
  960.         }
  961.         if(d->comment!=NULL && lineLength+strcspn(d->comment,NL)+6>=80){
  962.             fprintf(stream,"\\" NL);
  963.             lineLength=0;
  964.             lineLength+=fprintf(stream,"    ");
  965.         }
  966.         goto comment;
  967.     }
  968.     assert(d->dim[0]==0);
  969.     if(IsString(d->type))lineLength=0;
  970. fflush(stream);
  971.     lineLength+=fprintf(stream,"%s=",d->name);
  972. fflush(stream);
  973.     n=1;
  974.     switch(d->type){
  975.     case charType:
  976.         lineLength+=fprintf(stream,"%ld;",(long)*(char *)d->ptr);
  977.         break;
  978.     case unsignedCharType:
  979.         lineLength+=fprintf(stream,"%lu;",(unsigned long)*(unsigned char *)d->ptr);
  980.         break;
  981.     case shortType:
  982.         lineLength+=fprintf(stream,"%ld;",(long)*(short *)d->ptr);
  983.         break;
  984.     case unsignedShortType:
  985.         lineLength+=fprintf(stream,"%lu;",(unsigned long)*(unsigned short *)d->ptr);
  986.         break;
  987.     case longType:
  988.         lineLength+=fprintf(stream,"%ld;",*(long *)d->ptr);
  989.         break;
  990.     case unsignedLongType:
  991.         lineLength+=fprintf(stream,"%lu;",*(unsigned long *)d->ptr);
  992.         break;
  993.     case floatType:
  994.     #if SHORT_DOUBLE_OK
  995.     case shortDoubleType:
  996.     #endif
  997.     case doubleType:
  998.         if(flags&assignHexFloats){
  999.             string=BinaryToHex(typeSize[d->type],d->ptr,NULL);
  1000.             if(string==NULL){
  1001.                 if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes.\n"
  1002.                     ,here,2L*typeSize[d->type]);
  1003.                 else return assignMemoryError;
  1004.             }
  1005.             lineLength+=fprintf(stream,"\"%s\";",string);
  1006.             free(string);
  1007.         }else switch(d->type){
  1008.             case floatType: 
  1009.                 lineLength+=fprintf(stream,"%" /*FLT_DIG*/ "g;",*(float *)d->ptr);
  1010.                 break;
  1011.             #if SHORT_DOUBLE_OK
  1012.                 case shortDoubleType: 
  1013.                     lineLength+=fprintf(stream,"%" /*DBL_DIG*/ "g;"
  1014.                         ,*(short double *)d->ptr);
  1015.                     break;
  1016.             #endif
  1017.             case doubleType: 
  1018.                 lineLength+=fprintf(stream,"%" /*DBL_DIG*/ "g;",*(double *)d->ptr);
  1019.                 break;
  1020.         }
  1021.         break;
  1022.     case stringType:
  1023.         PrintQuotedString(stream,&lineLength,*(const char **)d->ptr);
  1024.         lineLength+=fprintf(stream,";");
  1025.         break;
  1026.     default:
  1027.         /*  It shouldn't be possible to arrive here. */
  1028.         PrintfExit("PrintAnAssignment: oops. \"%s\" of type (%s) fell through a crack.\n"
  1029.             ,d->name,typeName[d->type]);
  1030.         n=0;
  1031.     }
  1032. comment:
  1033.     if(d->comment!=NULL && !(flags&assignNoComment))
  1034.         lineLength+=fprintf(stream,"/* %s */",d->comment);
  1035.     return n;
  1036. }
  1037.  
  1038. int AssignmentLineWasBlank(void)
  1039. {
  1040.     extern int lineWasBlank;
  1041.  
  1042.     return lineWasBlank;
  1043. }
  1044.  
  1045. long ReadAssignmentFile(const char *filename,Description d[],short flags)
  1046. {
  1047.     long n=0,error;
  1048.     FILE *stream;
  1049.     static const char here[]="ReadAssignmentFile";
  1050.     
  1051.     stream=fopen(filename,"r");
  1052.     if(stream==NULL)return assignFileError;
  1053.     n=ReadAssignmentStream(stream,d,flags);
  1054.     fclose(stream);
  1055.     return n;
  1056.     
  1057.     #if 0
  1058.     if(!(flags&assignLocalCall)){
  1059.             /* First pass: scan for dimensions of unallocated Ptr variables.*/
  1060.             ReadAssignmentFile(filename,d,flags|assignScan|assignLocalCall);
  1061.             flags&=~assignScan;
  1062.             flags|=assignLocalCall;
  1063.             error=AllocateDescribedPtrVars(d,here,flags);
  1064.             if(error)return error;
  1065.             /* Second pass: read data again, including the newly allocated Ptr variables */
  1066.         }
  1067.         stream=fopen(filename,"r");
  1068.         if(stream==NULL)return assignFileError;
  1069.         do{
  1070.             n+=ReadAssignmentLine(stream,d,flags);
  1071.         }while(!feof(stream));
  1072.         fclose(stream);
  1073.         return n;
  1074.     #endif
  1075. }
  1076.  
  1077. long ReadAssignmentStream(FILE *stream,Description d[],short flags)
  1078. {
  1079.     long n=0,error;
  1080.     static const char here[]="ReadAssignmentStream";
  1081.     
  1082.     if(!(flags&assignLocalCall)){
  1083.         /* First pass: scan for dimensions of unallocated Ptr variables.*/
  1084.         long position=ftell(stream);
  1085.         ReadAssignmentStream(stream,d,flags|assignScan|assignLocalCall);
  1086.         flags&=~assignScan;
  1087.         flags|=assignLocalCall;
  1088.         fseek(stream,position,SEEK_SET);
  1089.         error=AllocateDescribedPtrVars(d,here,flags);
  1090.         if(error)return error;
  1091.         /* Second pass: read data again, including the newly allocated Ptr variables */
  1092.     }
  1093.     do{
  1094.         n+=ReadAssignmentLine(stream,d,flags);
  1095.     }while(!feof(stream));
  1096.     return n;
  1097. }
  1098.  
  1099. long ReadAssignmentBlock(FILE *stream,Description d[],short flags)
  1100. {
  1101.     long n=0,error;
  1102.     static const char here[]="ReadAssignmentBlock";
  1103.     
  1104.     if(!(flags&assignLocalCall)){
  1105.         /* First pass: scan for dimensions of unallocated Ptr variables.*/
  1106.         long position=ftell(stream);
  1107.         ReadAssignmentBlock(stream,d,flags|assignScan|assignLocalCall);
  1108.         flags&=~assignScan;
  1109.         flags|=assignLocalCall;
  1110.         fseek(stream,position,SEEK_SET);
  1111.         error=AllocateDescribedPtrVars(d,here,flags);
  1112.         if(error)return error;
  1113.         /* Second pass: read data again, including the newly allocated Ptr variables */
  1114.     }
  1115.     do{
  1116.         n+=ReadAssignmentLine(stream,d,flags);
  1117.     }while(!AssignmentLineWasBlank());
  1118.     return n;
  1119. }
  1120.  
  1121. static char *lineBuffer;    /* global because it would be messy to pass this address to
  1122.                             all the routines below that need to call ParsingError */
  1123. long ReadAssignmentLine(FILE *stream,Description description[],short flags)
  1124. {
  1125.     register Description *d;
  1126.     Description *vMatch;
  1127.     long j,n=0,nAssign;
  1128.     int i;
  1129.     char *s,*sOld;
  1130.     long ftellOld;
  1131.     void *ptr;
  1132.     extern int lineWasBlank;
  1133.     int hexAllowed,subscriptDims,outOfBounds,unknown;
  1134.     long subscript[ASSIGN_DIMS],index,error;
  1135.     Description scratchDescription,*dScratch;
  1136.     double scratchDouble;
  1137.     long scratchLong;
  1138.     void *scratchPtr;
  1139.     static const char here[]="ReadAssignmentLine";
  1140.     
  1141.     if(!(flags&assignLocalCall)){
  1142.         /* First pass: scan for dimensions of unallocated Ptr variables.*/
  1143.         long position=ftell(stream);
  1144.         ReadAssignmentLine(stream,description,flags|assignScan|assignLocalCall);
  1145.         flags&=~assignScan;
  1146.         flags|=assignLocalCall;
  1147.         fseek(stream,position,SEEK_SET);
  1148.         error=AllocateDescribedPtrVars(description,here,flags);
  1149.         if(error)return error;
  1150.         /* Second pass: read data again, including the newly allocated Ptr variables */
  1151.     }
  1152.     error=CheckDescriptions(description,here,flags);
  1153.     if(error)return error;
  1154.     lineWasBlank=TRUE;
  1155.     lineBuffer=(char *)malloc(BUFFER_SIZE);
  1156.     if(lineBuffer==NULL){
  1157.         if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes.\n"
  1158.             ,here,(long)BUFFER_SIZE);
  1159.         else return assignMemoryError;
  1160.     }
  1161.     if(stream==NULL)return 0;
  1162.     dScratch=&scratchDescription;
  1163.     s=NULL;
  1164.     do{
  1165.         /* parse the name */
  1166.         if(s==NULL)
  1167.             s=NextTokenInThisOrNextLine(stream,lineBuffer,s,flags&~assignNeedMore);
  1168.         else s=NextTokenInLine(stream,lineBuffer,s,flags);
  1169.         if(s==NULL){
  1170.             free(lineBuffer);
  1171.             return n;
  1172.         }
  1173.         *dScratch=Describe(unknownType,&scratchPtr,GetName(&s,flags),NULL);
  1174.         if(dScratch->name==NULL){
  1175.             free(lineBuffer);
  1176.             return assignMemoryError;
  1177.         }
  1178.         dScratch->sizedOnce=dScratch->sized=dScratch->malloced=0;
  1179.         d=dScratch;
  1180.         unknown=1;
  1181.         for(vMatch=&description[0];vMatch->type!=0;vMatch++){
  1182.             if(streq(dScratch->name,vMatch->name)){
  1183.                 d=vMatch;
  1184.                 unknown=0;
  1185.                 break;
  1186.             }
  1187.         }
  1188.         if(unknown && (flags&assignReportUnknown)){
  1189.             if(PRINTF_EXIT)ParsingError(s,"unknown variable");
  1190.             else {
  1191.                 free(lineBuffer);
  1192.                 return assignUnknownVariableError;
  1193.             }
  1194.         }
  1195.         hexAllowed=IsIntegral(d->type)&&!(flags&assignNoHexInts)
  1196.             || IsFloating(d->type)&&(flags&assignHexFloats);
  1197.         s=NextToken(stream,lineBuffer,s,flags);
  1198.         subscriptDims=0;
  1199.         for(i=0;i<ASSIGN_DIMS;i++)subscript[i]=0;
  1200.         outOfBounds=0;
  1201.         if(IsPtr(d->type) && !d->malloced)outOfBounds=1;
  1202.  
  1203.         /* parse subscripts, e.g. [123][3] */
  1204.         for(i=0;i<Dims(d) || !d->sized;i++){
  1205.             /* parse a subscript, e.g. [123] */
  1206.             if(*s != '['){
  1207.                 if(hexAllowed && subscriptDims==Dims(d)-1)break;
  1208.                 if(!d->sized)break;
  1209.                 if(PRINTF_EXIT)ParsingError(s,"expected \"[\"");
  1210.                 else {
  1211.                     free(lineBuffer);
  1212.                     return assignSubscriptError;
  1213.                 }
  1214.             }
  1215.             if(i==ASSIGN_DIMS){
  1216.                 if(PRINTF_EXIT)ParsingError(s,"too many dimensions");
  1217.                 else {
  1218.                     free(lineBuffer);
  1219.                     return assignSubscriptError;
  1220.                 }
  1221.             }
  1222.             s++;
  1223.             s=NextToken(stream,lineBuffer,s,flags);
  1224.             sOld=s;
  1225.             ftellOld=ftell(stream);
  1226.             subscript[i]=strtolN(s,&s,0);
  1227.             subscriptDims=i+1;
  1228.             if(s==sOld && ftellOld==ftell(stream)){
  1229.                 if(PRINTF_EXIT)ParsingError(s,"expected subscript");
  1230.                 else {
  1231.                     free(lineBuffer);
  1232.                     return assignSubscriptError;
  1233.                 }
  1234.             }
  1235.             j=subscript[i];
  1236.             if(i==Dims(d)-1)j-=d->firstElement;
  1237.             if(j<0 || d->sized && j>=d->dim[i]){
  1238.                 if(flags&assignReportUnknown){
  1239.                     if(PRINTF_EXIT)ParsingError(s,"subscript out of bounds: %s"
  1240.                         ,DescriptionNameDimensions(d));
  1241.                     else {
  1242.                         free(lineBuffer);
  1243.                         return assignSubscriptBoundsError;
  1244.                     }
  1245.                 }
  1246.                 outOfBounds=1;
  1247.             }
  1248.             s=NextToken(stream,lineBuffer,s,flags);
  1249.             if(*s != ']'){
  1250.                 if(PRINTF_EXIT)ParsingError(s,"expected \"]\"");
  1251.                 else {
  1252.                     free(lineBuffer);
  1253.                     return assignSubscriptError;
  1254.                 }
  1255.             }
  1256.             s++;
  1257.             s=NextToken(stream,lineBuffer,s,flags);
  1258.         }
  1259.  
  1260.         /* parse the equal sign */
  1261.         if(*s != '='){
  1262.             if(PRINTF_EXIT)ParsingError(s,"expected \"=\"");
  1263.             else {
  1264.                 free(lineBuffer);
  1265.                 return assignEqualsError;
  1266.             }
  1267.         }
  1268.         s++;
  1269.         
  1270.         /* parse the value */
  1271.         s=NextToken(stream,lineBuffer,s,flags);
  1272.         if(d->sized && Dims(d)!=subscriptDims 
  1273.             && !(*s=='"'  && Dims(d)-1==subscriptDims)){
  1274.             if(PRINTF_EXIT)
  1275.                 ParsingError(s,"\"%s\": wrong number of subscripts",DescriptionNameDimensions(d));
  1276.             else {
  1277.                 free(lineBuffer);
  1278.                 return assignSubscriptError;
  1279.             }
  1280.         }
  1281.         if(d->type==unknownType){
  1282.             /*  choose type that can accept the data, so we can parse and discard it. */
  1283.             if(*s=='"')d->type=stringType;
  1284.             else if(streq2(s,"0x") || streq2(s,"0X") || *s=='\''
  1285.                 || !(flags&assignNoHexInts) && *s=='"')d->type=longType;
  1286.             else d->type=doubleType;
  1287.         }
  1288.         if(unknown || outOfBounds){
  1289.             /* use scratch scalar to receive the assignment */
  1290.             short type=d->type;
  1291.             if(IsPtr(type))type+=charType-charPtrType;
  1292.             if(IsString(type))
  1293.                 *dScratch=Describe(type,&scratchPtr,d->name,NULL);
  1294.             else if(IsIntegral(type))
  1295.                 *dScratch=Describe(type,&scratchLong,d->name,NULL);
  1296.             else if(IsFloating(type))
  1297.                 *dScratch=Describe(type,&scratchDouble,d->name,NULL);
  1298.             d=dScratch;
  1299.             index=0;
  1300.             /* Note: d now inconsistent with subscript and subscriptDims */
  1301.         }else index=ElementIndex(d,subscript);
  1302.         ptr=ElementPtr(d,index);
  1303.         sOld=s;
  1304.         ftellOld=ftell(stream);
  1305.         nAssign=1;
  1306.         error=0;
  1307.         if(*s=='"' && !IsString(d->type) && hexAllowed){
  1308.             long bytes,elements;
  1309.             if(unknown || outOfBounds)
  1310.                 nAssign=HexAssignment(stream,lineBuffer,&s,d,0,index,&bytes,flags);
  1311.             else nAssign=HexAssignment(stream,lineBuffer,&s,d,subscriptDims,index
  1312.                 ,&bytes,flags);
  1313.             if(!unknown){
  1314.                 elements=bytes/typeSize[vMatch->type];
  1315.                 if(elements>1){
  1316.                     subscript[subscriptDims++]=elements-1;
  1317.                     error=GetDimensions(vMatch,subscriptDims,subscript,flags);
  1318.                     subscript[--subscriptDims]=0;
  1319.                 }else error=GetDimensions(vMatch,subscriptDims,subscript,flags);
  1320.             }
  1321.         }else{
  1322.             if(!unknown)error=GetDimensions(vMatch,subscriptDims,subscript,flags);
  1323.             else error=0;
  1324.             if(!error)switch(d->type){
  1325.             default:
  1326.                 assert(0 /* It shouldn't be possible to arrive here. */);
  1327.                 nAssign=0;
  1328.                 break;
  1329.             case charType:
  1330.             case charPtrType:
  1331.                 *(char *)ptr=strtolN(s,&s,0);
  1332.                 break;
  1333.             case unsignedCharType:
  1334.             case unsignedCharPtrType:
  1335.                 *(unsigned char *)ptr=strtoulN(s,&s,0);
  1336.                 break;
  1337.             case shortType:
  1338.             case shortPtrType:
  1339.                 *(short *)ptr=strtolN(s,&s,0);
  1340.                 break;
  1341.             case unsignedShortType:
  1342.             case unsignedShortPtrType:
  1343.                 *(unsigned short *)ptr=strtoulN(s,&s,0);
  1344.                 break;
  1345.             case longType:
  1346.             case longPtrType:
  1347.                 *(long *)ptr=strtolN(s,&s,0);
  1348.                 break;
  1349.             case unsignedLongType:
  1350.             case unsignedLongPtrType:
  1351.                 *(unsigned long *)ptr=strtoulN(s,&s,0);
  1352.                 break;
  1353.             case floatType:
  1354.             case floatPtrType:
  1355.                 *(float *)ptr=strtodN(s,&s);
  1356.                 break;
  1357.             #if SHORT_DOUBLE_OK
  1358.                 case shortDoubleType:
  1359.                 case shortDoublePtrType:
  1360.                     *(short double *)ptr=strtodN(s,&s);
  1361.                     break;
  1362.             #endif
  1363.             case doubleType:
  1364.             case doublePtrType:
  1365.                 *(double *)ptr=strtodN(s,&s);
  1366.                 break;
  1367.             case stringType:
  1368.                 if(d->malloced && !d->dim[0])free(*(char **)ptr);
  1369.                 *(char **)ptr=GetQuotedString(stream,lineBuffer,s,&s,flags);
  1370.                 d->malloced=1;
  1371.                 break;
  1372.             }
  1373.         }
  1374.         if(error==assignInconsistentDimensionsError){
  1375.             if(PRINTF_EXIT)ParsingError(s,"inconsistent dimensionality of \"%s\""
  1376.                 ,DescriptionNameDimensions(d));
  1377.             else {
  1378.                 free(lineBuffer);
  1379.                 return error;
  1380.             }
  1381.         }
  1382.         if(s==sOld && ftellOld==ftell(stream)){
  1383.             if(PRINTF_EXIT)ParsingError(s,"expected %s",typeName[d->type]);
  1384.             else {
  1385.                 free(lineBuffer);
  1386.                 return assignConstantError;
  1387.             }
  1388.         }
  1389.  
  1390.         /* parse the semicolon */
  1391.         s=NextToken(stream,lineBuffer,s,flags);
  1392.         if(*s!=';'){
  1393.             if(PRINTF_EXIT)ParsingError(s,"expected \";\"");
  1394.             else {
  1395.                 free(lineBuffer);
  1396.                 return assignSemicolonError;
  1397.             }
  1398.         }
  1399.         s++;
  1400.         
  1401.         if(ECHO_ASSIGNMENTS){
  1402.             if(unknown)printf("/*UNKNOWN: ");
  1403.             else if(outOfBounds)printf("/*OUT OF BOUNDS: ");
  1404.             if(!subscriptDims)PrintAnAssignment(stdout,d
  1405.                 ,(flags&~assignHexFloats)|assignNoComment);
  1406.             else PrintAnAssignmentOfElement(stdout,d,index
  1407.                 ,(flags&~assignHexFloats)|assignNoComment);
  1408.             if(outOfBounds || unknown)printf("*/ ");
  1409.         }
  1410.         dScratch->name=NULL;
  1411.         FreeADescribedVar(dScratch,flags);
  1412.         if(d!=dScratch)n+=nAssign;
  1413.     } while(1);
  1414. }
  1415.  
  1416. char *GetName(char **sPtr,short flags)
  1417. /*  Gets a variable's name.  */
  1418. {
  1419.     char *s,*sEnd,*name;
  1420.     long bytes=0,i;
  1421.     static const char here[]="ReadAssignmentLine/CheckADescription:GetName";
  1422.     
  1423.     s=*sPtr;
  1424.     if(ParseName(&s))if(PRINTF_EXIT)ParsingError(s,"expected name");
  1425.     sEnd=s;
  1426.     bytes=0;
  1427.     for(s=*sPtr;!isspace(*s) && s!=sEnd;s++)bytes++;
  1428.     name=(char *)malloc(bytes+1);
  1429.     if(name==NULL){
  1430.         if(PRINTF_EXIT)ParsingError(s,"%s: no room for %ld bytes.\n"
  1431.             ,here,bytes+1);
  1432.         else return NULL;
  1433.     }
  1434.     for(i=0;i<bytes;i++){
  1435.         while(isspace((*sPtr)[i]))(*sPtr)++;
  1436.         name[i]=(*sPtr)[i];
  1437.     }
  1438.     name[bytes]=0;
  1439.     *sPtr=s;
  1440.     return name;
  1441. }
  1442.  
  1443. int ParseName(char **sPtr)
  1444. /*
  1445. Parse a variable's name. It may be just a C identifier: alphabetic (or _)
  1446. followed by any number of alphanumeric (or _). Or it may be made up of multiple
  1447. identifiers joined by "." or "->" infix operators, e.g. "LP->p". 
  1448. */
  1449. {
  1450.     char *s;
  1451.     int error=0;
  1452.     
  1453.     s=*sPtr;
  1454.     while(*s!=0){
  1455.         for(;isspace(*s);s++);
  1456.         if(!(isalpha(*s)||*s=='_')){
  1457.             error=1;
  1458.             break;
  1459.         }
  1460.         for(;isalnum(*s)||*s=='_';s++);
  1461.         for(;isspace(*s);s++);
  1462.         if(*s=='.'){
  1463.             s++;
  1464.             continue;
  1465.         }
  1466.         if(streq2(s,"->")){
  1467.             s+=2;
  1468.             continue;
  1469.         }
  1470.         break;
  1471.     }
  1472.     *sPtr=s;
  1473.     return error;
  1474. }
  1475.  
  1476. long HexAssignment(FILE *stream,char *lineBuffer,char **sPtr,Description *d
  1477.     ,int subscriptDims,long index,long *bytesPtr,short flags)
  1478. {
  1479.     char *string,*s;
  1480.     long bytes,excess,n;
  1481.     short error;
  1482.     void *ptr;
  1483.     
  1484.     if(!(flags&assignScan) 
  1485.         && (subscriptDims<Dims(d)-1 || subscriptDims>Dims(d)))return assignSubscriptError;
  1486.     ptr=ElementPtr(d,index);
  1487.     /*
  1488.     Read in hex data.
  1489.     For scalar insist on exactly the right number of bytes.
  1490.     For array, if flags&assignReportUnknown then insist on exactly the right number
  1491.     of bytes; otherwise merely insist that the binary object size be a 
  1492.     multiple of the data element size.
  1493.     */
  1494.     s=*sPtr;
  1495.     string=GetQuotedString(stream,lineBuffer,s,&s,flags);
  1496.     *bytesPtr=strlen(string)/2;
  1497.     bytes=typeSize[d->type];
  1498.     if(subscriptDims==Dims(d)-1)bytes*=d->dim[subscriptDims];
  1499.     excess=strlen(string)-2*bytes;
  1500.     if(subscriptDims==Dims(d)-1 && !(excess>0 && (flags&assignReportUnknown)))
  1501.         excess=strlen(string)%(2*typeSize[d->type]);
  1502.     if(excess!=0 && !(flags&assignScan)){
  1503.         free(string);
  1504.         if(PRINTF_EXIT){
  1505.             if(excess>0)ParsingError(s,"\"%s=\", %ld extra hex digits",d->name,excess);
  1506.             else ParsingError(s,"\"%s=\", %ld too few hex digits",d->name,-excess);
  1507.         }else return assignHexError;
  1508.     }
  1509.     string[strlen(string)-strlen(string)%(2*typeSize[d->type])]=0;
  1510.     if(strlen(string)>2*bytes)string[2*bytes]=0;
  1511.     if(ptr!=NULL){
  1512.         error=HexToBinary(string,ptr);
  1513.         if(error){
  1514.             if(PRINTF_EXIT)ParsingError(s,"\"%s=\", hex string contains non hex char:\n\"%s\"\n"
  1515.                 ,d->name,string);
  1516.             else {
  1517.                 free(string);
  1518.                 return assignHexError;
  1519.             }
  1520.         }
  1521.         n=strlen(string)/(2*typeSize[d->type]);    /*  number of assigments */
  1522.     }else n=0;
  1523.     free(string);
  1524.     *sPtr=s;
  1525.     return n;
  1526. }
  1527.  
  1528. char *GetQuotedString(FILE *stream,char *lineBuffer,char *s,char **sPtr,short flags)
  1529. /*
  1530. Returns a newly malloced string copied from the lineBuffer (starting at s),
  1531. supplemented if necessary, by reading in further lines from the stream. The
  1532. translation from input to output mimics what would be performed by a C compiler.
  1533. Each line of input must fit in the BUFFER_SIZE byte lineBuffer, but the
  1534. resulting string (concatenated across continuations and adjacent strings) can be
  1535. of any length, limited only by how much space can be obtained by malloc. Returns
  1536. NULL if an error occurs (e.g. can't malloc needed space).
  1537. */
  1538. {
  1539.     char *newS,*oldString,*newString;
  1540.     size_t stringSize;
  1541.     long j;
  1542.     static const char here[]="GetQuotedString";
  1543.     
  1544.     /* parse the opening quote mark */
  1545.     if(*s!='"'){
  1546.         if(PRINTF_EXIT)ParsingError(s,"expected '\"'");
  1547.         else return NULL;
  1548.     }
  1549.     s++;
  1550.     stringSize=0;
  1551.     newS=newString=NULL;
  1552.     do{
  1553.         /* copy string, translating any backslash escapes */
  1554.         while(1){
  1555.             j=strcspn(s,"\\\"");
  1556.             if(stringSize==0 || strlen(newString)+j+2>stringSize){
  1557.                 /*  allocate a string that's big enough */
  1558.                 stringSize+=j;
  1559.                 stringSize*=2;
  1560.                 if(stringSize<BUFFER_SIZE)stringSize=BUFFER_SIZE;
  1561.                 oldString=newString;
  1562.                 newString=malloc(stringSize);
  1563.                 if(newString==NULL){
  1564.                     if(oldString!=NULL)free(oldString);
  1565.                     if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes.\n"
  1566.                         ,here,(long)stringSize);
  1567.                     else return NULL;
  1568.                 }
  1569.                 if(oldString!=NULL){
  1570.                     strcpy(newString,oldString);
  1571.                     free(oldString);
  1572.                 }
  1573.                 newS+=newString-oldString;
  1574.             }
  1575.             strncpy(newS,s,j);
  1576.             s+=j;
  1577.             newS+=j;
  1578.             *newS=0;
  1579.             if(streq2(s,"\\" NL)){
  1580.                 s+=2;
  1581.                 continue;
  1582.             }
  1583.             if(*s=='\\'){
  1584.                 *newS++=strtoc(s,&s);
  1585.                 *newS=0;
  1586.             }else break;
  1587.         };
  1588.         if(strlen(s)==0){
  1589.             s=ReadLine(lineBuffer,BUFFER_SIZE,stream,flags|assignNeedMore);
  1590.             continue;
  1591.         }
  1592.         /* parse the closing quote mark */
  1593.         if(*s!='"'){
  1594.             if(PRINTF_EXIT)ParsingError(s,"expected '\"'");
  1595.             else{
  1596.                 free(newString);
  1597.                 return NULL;
  1598.             }
  1599.         }
  1600.         s++;
  1601.         
  1602.         /* look for another opening quote mark. Concatenate adjacent strings */
  1603.         s=NextToken(stream,lineBuffer,s,flags);
  1604.         if(*s!='"')break;
  1605.         s++;
  1606.     }while(1);
  1607.     /* shrink string allocation down to what we actually used */
  1608.     stringSize=1+strlen(newString);
  1609.     newString=realloc(newString,stringSize);
  1610.     if(newString==NULL){
  1611.         if(PRINTF_EXIT)PrintfExit("%s: no room for %ld bytes.\n"
  1612.             ,here,(long)stringSize);
  1613.         else return NULL;
  1614.     }
  1615.     *sPtr=s;
  1616.     return newString;
  1617. }
  1618.  
  1619. void ParsingError(char *s,char *format,...)
  1620. {
  1621.     va_list args;
  1622.     long i;
  1623.  
  1624.     printf("\007\nReadAssignmentLine: ");
  1625.     va_start(args,format);
  1626.     vfprintf(stdout,format,args);
  1627.     va_end(args);
  1628.     if(s==NULL)PrintfExit(".\n");
  1629.     printf(". Quitting at ^.\n");
  1630.     printf("%s",lineBuffer);
  1631.     if(lineBuffer[strlen(lineBuffer)-1]!=NEWLINE)printf("...\n");
  1632.     s[0]='^';
  1633.     s[1]=0;
  1634.     s=lineBuffer;
  1635.     for(i=0;i<strlen(s)-1;i++)if(isgraph(s[i]))s[i]=' ';
  1636.     PrintfExit("%s\n",lineBuffer);
  1637. }
  1638.  
  1639. char *ReadLine(char *lineBuffer,size_t bufferSize,FILE *stream,short flags)
  1640. /*  The Boolean global lineWasBlank is initialized to true in ReadAssignmentLine(),  */
  1641. /*  falsified (if appropriate) here, and returned by AssignmentLineWasBlank(). */
  1642. {
  1643.     char *s;
  1644.     static const char whiteSpace[]=" \t\n\r\v\f";
  1645.     extern int lineWasBlank;
  1646.     
  1647.     s=fgets(lineBuffer,bufferSize,stream);
  1648.     if(s==NULL){
  1649.         if(flags&assignNeedMore){
  1650.             if(PRINTF_EXIT)ParsingError(s,"premature end of stream");
  1651.             else return NULL;
  1652.         }else return NULL;
  1653.     }
  1654.     if(strlen(s)!=strspn(s,whiteSpace))lineWasBlank=FALSE;
  1655.     if(strlen(s)>=bufferSize-1 && s[strlen(s)-1]!=NEWLINE){
  1656.         if(PRINTF_EXIT){
  1657.             s+=strlen(s);
  1658.             ParsingError(s,"line exceeds buffer size");
  1659.         }else return NULL;
  1660.     }
  1661.     if(ECHO_FILE)printf("%s",s);
  1662.     return s;
  1663. }
  1664.  
  1665. char *NextToken(FILE *stream,char *lineBuffer,char *s,short flags)
  1666. /*
  1667. Skip past white space and comments to the beginning of the next token,
  1668. reading as many lines as necessary.
  1669. */
  1670. {
  1671.     do{
  1672.         s=NextTokenInThisOrNextLine(stream,lineBuffer,s,flags|assignNeedMore);
  1673.         if(s!=NULL || feof(stream))return s;
  1674.     }while(1);
  1675. }
  1676.  
  1677. char *NextTokenInThisOrNextLine(FILE *stream,char *lineBuffer,char *s,short flags)
  1678. /*
  1679. Skip past white space and comments to the beginning of the next token,
  1680. reading one line if necessary. 
  1681. */
  1682. {
  1683.     s=NextTokenInLine(stream,lineBuffer,s,flags);
  1684.     if(s!=NULL)return s;
  1685.     s=ReadLine(lineBuffer,BUFFER_SIZE,stream,flags);
  1686.     s=NextTokenInLine(stream,lineBuffer,s,flags);
  1687.     return s;
  1688. }
  1689.  
  1690. char *NextTokenInLine(FILE *stream,char *lineBuffer,char *s,short flags)
  1691. /*
  1692. Skip past white space and comments to the beginning of the next token. NextTokenInLine
  1693. will read in new lines only if they are continuations of the current line (i.e.
  1694. it ends in "\") or if new lines are needed to find the end of an unfinished
  1695. comment. Input from stream is buffered one line at a time in lineBuffer, and s
  1696. points to the next unparsed character in that buffer.
  1697. */
  1698. {
  1699.     static char whiteSpace[]=" \t\n\r\v\f";
  1700.     char *sTemp;
  1701.  
  1702.     do{
  1703.         /* skip white space and comments */
  1704.         do {
  1705.             if(s==NULL || strlen(s)==0)goto endOfLine;
  1706.             s+=strspn(s,whiteSpace);
  1707.             if(strlen(s)==0)goto endOfLine;
  1708.             if(streq2(s,"/*")){
  1709.                 do{
  1710.                     sTemp=s;
  1711.                     s=strstr(s,"*/");
  1712.                     if(s!=NULL)break;
  1713.                     if(ECHO_COMMENTS)printf("%s",sTemp);
  1714.                     s=ReadLine(lineBuffer,BUFFER_SIZE,stream,flags|assignNeedMore);
  1715.                 } while(1);
  1716.                 s+=2;
  1717.                 if(ECHO_COMMENTS){
  1718.                     s[-1]=0;
  1719.                     printf("%s/",sTemp);
  1720.                 }
  1721.                 continue;
  1722.             }
  1723.             if(streq2(s,"//")){
  1724.                 if(ECHO_COMMENTS){
  1725.                     if(s[strlen(s)-1]==NEWLINE)s[strlen(s)-1]=0;
  1726.                     printf("%s",s);
  1727.                 }
  1728.                 goto endOfLine;
  1729.             }
  1730.             if(streq2(s,"\\" NL)){
  1731.                 if(ECHO_COMMENTS || ECHO_ASSIGNMENTS)printf(" \\\n");
  1732.                 s=ReadLine(lineBuffer,BUFFER_SIZE,stream,flags&~assignNeedMore);
  1733.                 continue;
  1734.             }
  1735.             break;
  1736.         } while(1);
  1737.         return s;    /* normal return */
  1738.     }while(1);
  1739. endOfLine:
  1740.     if((ECHO_ASSIGNMENTS || ECHO_COMMENTS) && s!=NULL)printf("\n");
  1741.     return NULL;
  1742. }
  1743.  
  1744. double strtodN(char *s,char **sPtr)
  1745. /*
  1746. Supplement standard C routine strtod() by also handling NAN and INF. Any specification
  1747. of the NAN's type (1 to 255) is ignored, whether in THINK C style, e.g. NANFF, or in
  1748. MPW C style, e.g. Nan[255].
  1749. */
  1750. {
  1751.     if(streq2(s,"NAN")||streq2(s,"NaN")||streq2(s,"Nan")||streq2(s,"nan")){
  1752.         s+=3;
  1753.         s+=strspn(s,"0123456789abcdefABCDEF[]");
  1754.         *sPtr=s;
  1755.         return NAN;
  1756.     }
  1757.     if(strncmp(s,"INF",3)==0||strncmp(s,"Inf",3)==0||strncmp(s,"inf",3)==0){
  1758.         s+=3;
  1759.         *sPtr=s;
  1760.         return INF;
  1761.     }
  1762.     if(strncmp(s,"-INF",4)==0||strncmp(s,"-Inf",4)==0||strncmp(s,"-inf",4)==0){
  1763.         s+=4;
  1764.         *sPtr=s;
  1765.         return -INF;
  1766.     }
  1767.     return strtod(s,sPtr);
  1768. }
  1769.  
  1770. long strtolN(char *s,char **sPtr,short flags)
  1771. /* Supplement standard C routine strtol() by handling quoted char, e.g. 'e' */
  1772. {
  1773.     long j;
  1774.     
  1775.     if(*s!='\'') return strtol(s,sPtr,flags);
  1776.     s++;
  1777.     j=strtoc(s,&s);
  1778.     if(*s!='\''){
  1779.         if(PRINTF_EXIT)ParsingError(s,"expected \"'\"");
  1780.         else return 0;
  1781.     }
  1782.     *sPtr=s+1;
  1783.     return j;
  1784. }    
  1785.  
  1786. unsigned long strtoulN(char *s,char **sPtr,short flags)
  1787. /* Supplement standard C routine by handling quoted char, e.g. 'e' */
  1788. {
  1789.     unsigned long j;
  1790.     
  1791.     if(*s!='\'') return strtoul(s,sPtr,flags);
  1792.     s++;
  1793.     j=strtoc(s,&s);
  1794.     j &= 255L;        /* strip off any sign extension, since char was signed */
  1795.     if(*s!='\''){
  1796.         if(PRINTF_EXIT)ParsingError(s,"expected \"'\"");
  1797.         else return 0;
  1798.     }
  1799.     *sPtr=s+1;
  1800.     return j;
  1801. }    
  1802.  
  1803. char strtoc(char *s,char **sPtr)
  1804. /* Extract a character from a string, translating backslash escapes */
  1805. {
  1806.     char c;
  1807.     
  1808.     startAgain:
  1809.     if(*s!='\\'){
  1810.         if(*s==0){
  1811.             *sPtr=s;
  1812.             return -1;
  1813.         }
  1814.         *sPtr=s+1;
  1815.         return *s;
  1816.     }
  1817.     s++;
  1818.     switch(s++[0]){
  1819.     case 'n': c='\n'; break;
  1820.     case 'r': c='\r'; break;
  1821.     case 't': c='\t'; break;
  1822.     case 'b': c='\b'; break;
  1823.     case 'v': c='\v'; break;
  1824.     case 'f': c='\f'; break;
  1825.     case 'a': c='\a'; break;
  1826.     case 'x': c=strtol(s,&s,16); break;
  1827.     case '0': case '1':case '2': case '3':case '4': case '5':case '6': case '7':
  1828.         c=strtol(s-1,&s,8); break;
  1829.     case NEWLINE: s++; goto startAgain;
  1830.     default: c=s[-1]; break;
  1831.     }
  1832.     *sPtr=s;
  1833.     return c;
  1834. }
  1835.  
  1836. void PrintQuotedString(FILE *stream,short *lineLengthPtr,const char *s)
  1837. {
  1838.     *lineLengthPtr+=fprintf(stream,"\"");
  1839.     do{
  1840.         for(;*s!=0 && *lineLengthPtr+1+(isprint(*s)?1:(isspace(*s)?2:4))<=80;s++){
  1841.             if(isprint(*s)) *lineLengthPtr+=fprintf(stream,"%c",(PRINTF_INT)*s);
  1842.             else switch(*s){
  1843.             case '\n': *lineLengthPtr+=fprintf(stream,"\\n"); break;
  1844.             case '\t': *lineLengthPtr+=fprintf(stream,"\\t"); break;
  1845.             case '\r': *lineLengthPtr+=fprintf(stream,"\\r"); break;
  1846.             case '\v': *lineLengthPtr+=fprintf(stream,"\\v"); break;
  1847.             case '\f': *lineLengthPtr+=fprintf(stream,"\\f"); break;
  1848.             default: *lineLengthPtr+=fprintf(stream,"\\%03lo"
  1849.                 ,(unsigned long)*(unsigned char *)s); break;
  1850.             }
  1851.         }
  1852.         if(*s!=0){
  1853.             fprintf(stream,"\"" NL);
  1854.             *lineLengthPtr=0;
  1855.             *lineLengthPtr+=fprintf(stream,"    \"");
  1856.         }
  1857.     }while(*s!=0);
  1858.     *lineLengthPtr+=fprintf(stream,"\"");
  1859. }
  1860.  
  1861. #if 0
  1862.     /*  A longer, Macintosh-savvy, version of this routine, PrintfExit.c, */
  1863.     /*  is part of the VideoToolbox. */
  1864.     int PrintfExit(const char *format,...)
  1865.     {
  1866.         va_list args;
  1867.         int i;
  1868.       
  1869.         va_start(args,format);
  1870.         i=vfprintf(stdout,format,args);
  1871.         va_end(args);
  1872.         exit(1);
  1873.     }
  1874. #endif
  1875.